home *** CD-ROM | disk | FTP | other *** search
/ Programming an RTS Game with Direct3D / Programming an RTS Game with Direct3D.iso / Examples / Chapter 16 / Example 16.1 / unit.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-07-27  |  17.2 KB  |  735 lines

  1. #include "unit.h"
  2. #include "groupai.h"
  3. #include "player.h"
  4.  
  5. std::vector<SKINNEDMESH*> unitMeshes;
  6.  
  7. void LoadUnitResources(IDirect3DDevice9* m_pDevice)
  8. {
  9.     std::vector<std::string> fnames;
  10.  
  11.     fnames.push_back("units/drone.x");
  12.     fnames.push_back("units/soldier.x");
  13.     fnames.push_back("units/magician.x");
  14.  
  15.     for(int i=0;i<fnames.size();i++)
  16.     {
  17.         SKINNEDMESH *newMesh = new SKINNEDMESH();
  18.         newMesh->Load((char*)fnames[i].c_str(), m_pDevice);
  19.         unitMeshes.push_back(newMesh);
  20.     }
  21. }
  22.  
  23. void UnloadUnitResources()
  24. {
  25.     for(int i=0;i<unitMeshes.size();i++)
  26.         if(unitMeshes[i] != NULL)
  27.             delete unitMeshes[i];
  28.  
  29.     unitMeshes.clear();
  30. }
  31.  
  32. //////////////////////////////////////////////////////////////////////////////////
  33. //                                UNIT                                            //
  34. //////////////////////////////////////////////////////////////////////////////////
  35.  
  36. UNIT::UNIT(int _type, int _team, INTPOINT mp, TERRAIN *_terrain, PLAYER *_player, IDirect3DDevice9* Dev) : MAPOBJECT()
  37. {
  38.     m_type = _type;
  39.     m_team = _team;
  40.     m_mappos = mp;
  41.     m_pTerrain = _terrain;
  42.     m_pDevice = Dev;
  43.     m_mapsize.Set(0, 0);
  44.     m_time = m_pauseTime = 0.0f;
  45.     m_animation = m_activeWP = 0;
  46.     m_rotation = D3DXVECTOR3(0.0f, 0.0f, 0.0f);    
  47.     m_isBuilding = m_moving = false;
  48.     m_movePrc = 0.0f;
  49.     m_state = STATE_IDLE;
  50.     m_attackTime = 0.0f;
  51.     m_pPlayer = _player;
  52.     m_mana = 0.0f;
  53.  
  54.     //Random sized units
  55.     float y  = (((rand()%100) / 1000.0f) - 0.05f) * 0.333f;
  56.     float xz = (((rand()%100) / 1000.0f) - 0.05f) * 0.333f;
  57.     m_scale = D3DXVECTOR3(0.2f + xz, 0.2f + y, 0.2f + xz);
  58.  
  59.     if(m_pTerrain != NULL)
  60.     {
  61.         m_position = m_pTerrain->GetWorldPos(m_mappos);
  62.         MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  63.         if(tile != NULL)tile->m_pMapObject = this;
  64.     }
  65.     else m_position = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
  66.  
  67.     if(m_type == WORKER)
  68.     {
  69.         m_hp = m_hpMax = 100;
  70.         m_range = 1;
  71.         m_damage = 5;
  72.         m_sightRadius = 7;
  73.         m_speed = 1.0f;
  74.         m_name = "Farmer";
  75.     }    
  76.     else if(m_type == SOLDIER)
  77.     {
  78.         m_hp = m_hpMax = 180;
  79.         m_range = 1;
  80.         m_damage = 12;
  81.         m_sightRadius = 8;
  82.         m_speed = 0.8f;
  83.         m_name = "Soldier";
  84.     }    
  85.     else if(m_type == MAGICIAN)
  86.     {
  87.         m_hp = m_hpMax = 100;
  88.         m_range = 4;
  89.         m_damage = 8;
  90.         m_sightRadius = 10;
  91.         m_speed = 1.1f;
  92.         m_name = "Magician";
  93.     }
  94.  
  95.     m_pAnimControl = unitMeshes[m_type]->GetAnimationControl();
  96.     if(m_pAnimControl != NULL)
  97.         m_pAnimControl->ResetTime();
  98.  
  99.     SetAnimation("Still");
  100. }
  101.  
  102. UNIT::~UNIT()
  103. {
  104.     if(m_pTerrain)m_pTerrain->m_updateSight = true;
  105. }
  106.  
  107. void UNIT::Render()
  108. {
  109.     try
  110.     {
  111.         if(m_visible && m_type < unitMeshes.size() && unitMeshes[m_type] != NULL)
  112.         {
  113.             SetAnimation(m_animation);
  114.             unitMeshes[m_type]->SetPose(GetWorldMatrix(), m_pAnimControl, m_time);
  115.             unitMeshes[m_type]->Render(NULL);
  116.             m_time = 0.0f;
  117.  
  118.             //Extract staff position
  119.             if(m_type == MAGICIAN)
  120.             {
  121.                 BONE *staff = unitMeshes[m_type]->FindBone("Staff");
  122.  
  123.                 if(staff != NULL)
  124.                 {
  125.                     D3DXMATRIX mat = staff->CombinedTransformationMatrix;
  126.                     m_staffPos = D3DXVECTOR3(mat(3, 0), mat(3, 1), mat(3, 2));
  127.                 }
  128.             }    
  129.         }
  130.     }
  131.     catch(...)
  132.     {
  133.         debug.Print("Error in UNIT::Render()");
  134.     }    
  135. }
  136.  
  137. void UNIT::Update(float deltaTime)
  138. {
  139.     try
  140.     {
  141.         if(m_type == MAGICIAN && m_mana < 50)
  142.             m_mana += deltaTime;
  143.  
  144.         //Pause the units...
  145.         if(m_pauseTime > 0.0f && !m_dead)
  146.         {
  147.             m_pauseTime -= deltaTime;
  148.             return;
  149.         }
  150.  
  151.         m_staffPos = m_position + D3DXVECTOR3(0.0f, 1.0f, 0.0f);
  152.  
  153.         //update unit animation time
  154.         if(m_dead)
  155.         {
  156.             m_selected = false;
  157.             m_state = STATE_DEAD;
  158.             m_time += deltaTime * 0.3f;
  159.  
  160.             //Shrink sightradius when a unit has died
  161.             int oldSr = m_sightRadius;
  162.             if(m_sightRadius > 0.0f)m_sightRadius -= deltaTime;
  163.             if(m_sightRadius < 0.0f)m_sightRadius = 0.0f;
  164.             if(oldSr != (int)m_sightRadius)m_pTerrain->m_updateSight = true;
  165.                 
  166.             float duration = unitMeshes[m_type]->GetAnimationDuration(ANIM_DIE) - 0.05f;
  167.             if(m_pAnimControl->GetTime() > duration)
  168.             {                
  169.                 m_position.y -= deltaTime * 0.02f;
  170.                 m_time = 0.0f;
  171.                 m_pAnimControl->SetTrackPosition(0, duration);
  172.             }
  173.         }
  174.         else if(m_state == STATE_ATTACK)
  175.         {
  176.             m_time += deltaTime * 0.5f;
  177.             float attackDuration = unitMeshes[m_type]->GetAnimationDuration(ANIM_ATTACK);
  178.             if(m_pAnimControl->GetTime() > attackDuration)
  179.             {
  180.                 m_pAnimControl->SetTrackPosition(0, 0.0);
  181.                 m_time = 0.0f;
  182.             }
  183.  
  184.             m_attackTime += deltaTime;
  185.         }
  186.         else m_time += deltaTime * 0.8f * m_speed;
  187.  
  188.         //Check that the unit is standing on a walkable tile
  189.         MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  190.         if(tile == NULL || !tile->m_walkable)
  191.         {
  192.             m_mappos = m_pTerrain->GetClosestFreeTile(m_mappos, m_mappos);
  193.             MoveUnit(m_mappos);
  194.             m_position = m_pTerrain->GetWorldPos(m_mappos);
  195.             m_moving = false;
  196.         }
  197.  
  198.         //if the unit is moving...
  199.         if(m_moving && !m_dead)
  200.         {
  201.             if(m_movePrc < 1.0f)m_movePrc += deltaTime * m_speed;
  202.             if(m_movePrc > 1.0f)m_movePrc = 1.0f;
  203.             
  204.             //Goal reached
  205.             if(m_movePrc == 1.0f)
  206.             {
  207.                 if(m_activeWP + 1 >= m_path.size())
  208.                 {
  209.                     m_moving = false;
  210.                     SetAnimation("Still");                
  211.                     if(m_mappos != m_finalGoal)
  212.                         Goto(m_finalGoal, false, true, m_state);
  213.                 }
  214.                 else if(!CheckCollision(m_path[m_activeWP + 1]) && !UnitAI(true)) //Next Waypoint
  215.                 {            
  216.                     m_activeWP++;
  217.                     SetAnimation("Run");
  218.                     MoveUnit(m_path[m_activeWP]);
  219.                 }            
  220.             }
  221.  
  222.             //Interpolate position between m_lastWP and m_nextWP
  223.             m_position = m_lastWP * (1.0f - m_movePrc) + m_nextWP * m_movePrc;
  224.         }
  225.     }
  226.     catch(...)
  227.     {
  228.         debug.Print("Error in UNIT::Update()");
  229.     }
  230. }
  231.  
  232. bool UNIT::CheckCollision(INTPOINT mp)
  233. {
  234.     MAPTILE *tile = m_pTerrain->GetTile(mp);
  235.     if(tile == NULL || m_dead)return false;
  236.     
  237.     try
  238.     {
  239.         if(tile->m_pMapObject != NULL && tile->m_pMapObject != this)    //Collision with another unit
  240.         {
  241.             UNIT *otherUnit = (UNIT*)tile->m_pMapObject;
  242.  
  243.             //The other unit is moving
  244.             if(otherUnit->m_moving && otherUnit->m_pauseTime <= 0.0f && m_speed <= otherUnit->m_speed)
  245.             {
  246.                 //Pause the unit and wait for the other one to move 
  247.                 Pause((100 + rand()%200) / 1000.0f);
  248.                 m_path.clear();
  249.             }
  250.             else    //Recalculate path
  251.             {
  252.                 //Find next unoccupied walkable tile
  253.                 INTPOINT tempGoal = m_mappos;
  254.                 for(int i=m_activeWP+1;i<m_path.size();i++)
  255.                 {
  256.                     MAPTILE *tile = m_pTerrain->GetTile(m_path[i]);
  257.                     if(tile != NULL)
  258.                         if(tile->m_walkable && tile->m_pMapObject == NULL)
  259.                         {
  260.                             tempGoal = m_path[i];
  261.                             break;
  262.                         }
  263.                 }
  264.  
  265.                 //No available tile found 
  266.                 if(tempGoal == m_mappos)
  267.                 {
  268.                     //Move to tile closest to the original goal
  269.                     INTPOINT newGoal = m_pTerrain->GetClosestFreeTile(m_finalGoal, m_mappos);
  270.  
  271.                     if(newGoal == m_mappos || m_mappos.Distance(m_finalGoal) < 2.0f || !m_pTerrain->Within(newGoal))
  272.                     {
  273.                         m_moving = false;
  274.                         SetAnimation("Still");
  275.                         m_state = STATE_IDLE;
  276.                     }
  277.                     else Goto(newGoal, false, true, m_state);
  278.                 }
  279.                 else 
  280.                 {
  281.                     //Move to tempGoal to avoid unit, then continue to m_finalGoal
  282.                     Goto(tempGoal, true, false, m_state); 
  283.                 }
  284.             }
  285.  
  286.             return true;    //A Collision happened
  287.         }
  288.     }
  289.     catch(...)
  290.     {
  291.         debug.Print("Error in UNIT::CheckCollision()");
  292.     }
  293.  
  294.     return false;        //No Collision
  295. }
  296.  
  297. void UNIT::Goto(INTPOINT mp, bool considerUnits, bool _finalGoal, int newState)
  298. {
  299.     if(m_pTerrain == NULL || m_dead || !m_pTerrain->Within(mp))return;
  300.     if(_finalGoal)m_finalGoal = mp;
  301.     
  302.     try
  303.     {
  304.         //Clear old path
  305.         m_path.clear();
  306.         m_activeWP = 0;
  307.         m_state = newState;
  308.  
  309.         if(m_moving)        //If unit is currently moving
  310.         {
  311.             //Finish the active waypoint 
  312.             m_path.push_back(m_mappos);
  313.             std::vector<INTPOINT> tmpPath = m_pTerrain->GetPath(m_mappos, mp, considerUnits, this);
  314.  
  315.             //add new path
  316.             for(int i=0;i<tmpPath.size();i++)
  317.                 m_path.push_back(tmpPath[i]);
  318.         }
  319.         else        //Create new path from scratch...
  320.         {
  321.             m_path = m_pTerrain->GetPath(m_mappos, mp, considerUnits, this);
  322.  
  323.             if(m_path.size() > 0)        //if a path was found
  324.             {
  325.                 m_moving = true;
  326.  
  327.                 //Check that the next tile is free
  328.                 if(!CheckCollision(m_path[m_activeWP]))
  329.                 {
  330.                     MoveUnit(m_path[m_activeWP]);
  331.                     SetAnimation("Run");                
  332.                 }
  333.             }
  334.             else Pause(0.1f);
  335.         }
  336.     }
  337.     catch(...)
  338.     {
  339.         debug.Print("Error in UNIT::Goto()");
  340.     }
  341. }
  342.  
  343. void UNIT::MoveUnit(INTPOINT to)
  344. {
  345.     if(!m_pTerrain->Within(to))return;
  346.  
  347.     try
  348.     {
  349.         if(m_mappos.Distance(to) >= 2.0f)
  350.         {
  351.             m_state = STATE_IDLE;
  352.             MoveUnit(m_mappos);
  353.             m_moving = false;
  354.             return;
  355.         }
  356.  
  357.         m_lastWP = m_pTerrain->GetWorldPos(m_mappos);
  358.         m_rotation = GetDirection(m_mappos, to);
  359.  
  360.         //Clear old MAPTILE unit pointer
  361.         MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  362.         if(tile != NULL)tile->m_pMapObject = NULL;
  363.  
  364.         m_mappos = to;    //New m_mappos
  365.         m_movePrc = 0.0f;
  366.         m_nextWP = m_pTerrain->GetWorldPos(m_mappos);
  367.  
  368.         //Set new MAPTILE unit pointer
  369.         tile = m_pTerrain->GetTile(m_mappos);
  370.         if(tile != NULL)tile->m_pMapObject = this;
  371.  
  372.         m_pTerrain->m_updateSight = true;
  373.     }
  374.     catch(...)
  375.     {
  376.         debug.Print("Error in UNIT::MoveUnit()");
  377.     }
  378. }
  379.  
  380. void UNIT::Pause(float time)
  381. {
  382.     SetAnimation("Still");
  383.     m_pauseTime = time;
  384. }
  385.  
  386. BBOX UNIT::GetBoundingBox()
  387. {
  388.     if(m_type == 0)        //Farmer
  389.         return BBOX(m_position + D3DXVECTOR3(0.3f, 1.0f, 0.3f), m_position - D3DXVECTOR3(0.3f, 0.0f, 0.3f));
  390.     else if(m_type == 1)    //Soldier
  391.         return BBOX(m_position + D3DXVECTOR3(0.35f, 1.2f, 0.35f), m_position - D3DXVECTOR3(0.35f, 0.0f, 0.35f));
  392.     else if(m_type == 2)    //Magician
  393.         return BBOX(m_position + D3DXVECTOR3(0.3f, 1.1f, 0.3f), m_position - D3DXVECTOR3(0.3f, 0.0f, 0.3f));
  394. }
  395.  
  396. D3DXMATRIX UNIT::GetWorldMatrix()
  397. {
  398.     D3DXMATRIX s, p, r;
  399.     D3DXMatrixTranslation(&p, m_position.x, m_position.y, m_position.z);
  400.     D3DXMatrixRotationYawPitchRoll(&r, m_rotation.y, m_rotation.x, m_rotation.z);
  401.     D3DXMatrixScaling(&s, m_scale.y, m_scale.x, m_scale.z);
  402.     return s * r * p;
  403. }
  404.  
  405. D3DXVECTOR3 UNIT::GetDirection(INTPOINT p1, INTPOINT p2)
  406. {
  407.     int dx = p2.x - p1.x, dy = p2.y - p1.y;
  408.     
  409.     if(dx < 0 && dy > 0)    return D3DXVECTOR3(0.0f, D3DX_PI/4,        0.0f); 
  410.     if(dx == 0 && dy > 0)    return D3DXVECTOR3(0.0f, 0.0f,            0.0f);
  411.     if(dx > 0 && dy > 0)    return D3DXVECTOR3(0.0f, -D3DX_PI/4,    0.0f); 
  412.     if(dx > 0 && dy == 0)    return D3DXVECTOR3(0.0f, -D3DX_PI/2,    0.0f);
  413.     if(dx > 0 && dy < 0)    return D3DXVECTOR3(0.0f, (-D3DX_PI/4)*3,0.0f); 
  414.     if(dx == 0 && dy < 0)    return D3DXVECTOR3(0.0f, D3DX_PI,        0.0f);
  415.     if(dx < 0 && dy < 0)    return D3DXVECTOR3(0.0f, (D3DX_PI/4)*3,    0.0f); 
  416.     if(dx < 0 && dy == 0)    return D3DXVECTOR3(0.0f, D3DX_PI/2,        0.0f);
  417.  
  418.     return m_rotation;
  419. }
  420.  
  421. void UNIT::SetAnimation(char name[])
  422. {
  423.     ID3DXAnimationSet *anim = NULL;
  424.     m_animation = 0;
  425.  
  426.     for(int i=0;i<m_pAnimControl->GetMaxNumAnimationSets();i++)
  427.     {
  428.         anim = NULL;
  429.         m_pAnimControl->GetAnimationSet(i, &anim);
  430.  
  431.         if(anim != NULL)
  432.         {
  433.             if(strcmp(name, anim->GetName()) == 0)
  434.             {
  435.                 m_pAnimControl->SetTrackAnimationSet(0, anim);
  436.                 m_pAnimControl->SetTrackPosition(0, 0.0f);
  437.                 m_pAnimControl->ResetTime();
  438.                 m_animation = i;
  439.             }
  440.             anim->Release();
  441.         }
  442.     }
  443. }
  444.  
  445. void UNIT::SetAnimation(int index)
  446. {
  447.     ID3DXAnimationSet *anim = NULL;
  448.     m_pAnimControl->GetAnimationSet(index, &anim);
  449.     if(anim != NULL)m_pAnimControl->SetTrackAnimationSet(0, anim);
  450.     anim->Release();
  451. }
  452.  
  453. bool UNIT::UnitAI(bool newMapTile)
  454. {
  455.     if(m_dead)return false;
  456.  
  457.     bool result = false;
  458.  
  459.     try
  460.     {
  461.         std::vector<MAPOBJECT*> enemies = GetTargetsWithinRange(m_sightRadius);
  462.         
  463.         //Report enemy sightings
  464.         if(m_pGroup != NULL && !enemies.empty())
  465.             m_pGroup->EnemiesSpotted(enemies);
  466.  
  467.         switch(m_state)
  468.         {    
  469.             case STATE_IDLE:
  470.             {
  471.                 m_animation = ANIM_STILL;
  472.  
  473.                 if(enemies.size() > 0)
  474.                 {
  475.                     m_pTarget = BestTargetToAttack(enemies);
  476.                     m_state = STATE_ATTACK;
  477.                     result = true;
  478.                 }
  479.                 
  480.                 Heal();
  481.  
  482.                 break;
  483.             }
  484.             case STATE_MOVING:
  485.             {
  486.                 m_animation = ANIM_RUN;
  487.  
  488.                 if(!m_moving)
  489.                 {
  490.                     m_state = STATE_IDLE;
  491.                     m_animation = ANIM_STILL;
  492.                 }
  493.  
  494.                 break;
  495.             }
  496.             case STATE_DEAD:
  497.             {
  498.                 //Stay dead
  499.                 m_dead = true;
  500.                 break;
  501.             }
  502.             case STATE_SEARCH:
  503.             {
  504.                 if(enemies.size() > 1)
  505.                     m_pTarget = BestTargetToAttack(enemies);
  506.  
  507.                 if(m_pTarget == NULL || m_pTarget->m_dead)
  508.                 {
  509.                     m_state = STATE_IDLE;
  510.                     break;
  511.                 }
  512.  
  513.                 //Attack if target is within range
  514.                 if(newMapTile || !m_moving)
  515.                 {
  516.                     if(m_mappos.inRect(m_pTarget->GetMapRect(m_range)))
  517.                     {
  518.                         m_state = STATE_ATTACK;
  519.                         SetAnimation("Attack");
  520.                         m_moving = false;
  521.                         m_attackTime = 0.0f;
  522.                         m_time = unitMeshes[m_type]->GetAnimationDuration(ANIM_ATTACK);
  523.                         m_rotation = GetDirection(m_mappos, m_pTarget->m_mappos);
  524.                         result = true;
  525.                     }
  526.                     else
  527.                     {
  528.                         INTPOINT attackPos = m_pTarget->GetAttackPos(m_mappos); 
  529.                         if(!m_pTerrain->Within(attackPos))
  530.                         {
  531.                             m_state = STATE_IDLE;
  532.                             break;
  533.                         }
  534.  
  535.                         //Update path
  536.                         if(!m_moving || attackPos != m_finalGoal)
  537.                         {                    
  538.                             bool considerUnit = m_mappos.Distance(m_pTarget->m_mappos) <= 5;
  539.                             Goto(attackPos, considerUnit, true, m_state);
  540.                         }
  541.                     }
  542.                 }
  543.  
  544.                 break;
  545.             }
  546.             case STATE_ATTACK:
  547.             {
  548.                 if(m_pTarget == NULL || m_pTarget->m_dead){m_state = STATE_IDLE; return false;}
  549.  
  550.                 //Heal other units
  551.                 Heal();
  552.  
  553.                 if(m_mappos.inRect(m_pTarget->GetMapRect(m_range)))
  554.                 {
  555.                     //Attack
  556.                     if(m_attackTime > 3.0f)
  557.                     {
  558.                         m_attackTime = m_time = 0.0f;
  559.                         m_animation = ANIM_ATTACK;
  560.                         m_rotation = GetDirection(m_mappos, m_pTarget->m_mappos);
  561.                         m_pAnimControl->ResetTime();
  562.  
  563.                         //Magician Fireball effect
  564.                         if(m_type == MAGICIAN)
  565.                             effects.push_back(new EFFECT_FIREBALL(m_pDevice, &m_staffPos, m_pTarget, this));
  566.                         else m_pTarget->Damage(m_damage, this);
  567.                     }
  568.                 }
  569.                 else m_state = STATE_SEARCH;
  570.  
  571.                 break;
  572.             }
  573.             case STATE_RETREAT:
  574.             {    
  575.                 if(!m_moving)
  576.                     m_state = STATE_IDLE;
  577.  
  578.                 break;
  579.             }
  580.             case STATE_GOTO_BUILD:
  581.             {        
  582.                 if(!m_moving)Goto(m_buildingPosition, false, true, STATE_GOTO_BUILD);
  583.  
  584.                 BUILDING *temp = new BUILDING(m_buildingToPlace, m_team, true, m_buildingPosition, m_pTerrain, m_pPlayer, false, m_pDevice);
  585.  
  586.                 if(m_mappos.inRect(temp->GetMapRect(0)))
  587.                 {
  588.                     if(m_pPlayer->money < GetCost(m_buildingToPlace, true))
  589.                     {
  590.                         m_state = STATE_IDLE;                
  591.                     }
  592.                     else
  593.                     {
  594.                         m_selected = false;
  595.                         m_pPlayer->RemoveMapObject(this);
  596.                         BUILDING *build = (BUILDING*)m_pPlayer->AddMapObject(m_buildingToPlace, m_buildingPosition, true, false);
  597.                         build->m_pTarget = this;
  598.                         m_pPlayer->money -= GetCost(m_buildingToPlace, true);
  599.                         m_state = STATE_BUILD;
  600.                     }
  601.                 }
  602.  
  603.                 delete temp;
  604.  
  605.                 break;
  606.             }
  607.             case STATE_BUILD:
  608.             {    
  609.                 m_state = STATE_IDLE;
  610.                 break;
  611.             }
  612.         }
  613.     }
  614.     catch(...)
  615.     {
  616.         debug.Print("Error in UNIT::UnitAI()");
  617.     }
  618.  
  619.     return result;
  620. }
  621.  
  622. void UNIT::Attack(MAPOBJECT *_target)
  623. {
  624.     if(m_dead || _target == NULL)return;
  625.  
  626.     m_pTarget = _target;
  627.     m_state = STATE_SEARCH;
  628.     UnitAI(false);
  629. }
  630.  
  631. void UNIT::ConstructBuilding(int buildToPlace, INTPOINT pos)
  632. {
  633.     if(m_type != WORKER)return;
  634.  
  635.     Goto(pos, false, true, STATE_GOTO_BUILD);
  636.     m_buildingToPlace = buildToPlace;
  637.     m_buildingPosition = pos;
  638.     UnitAI(false);
  639. }
  640.  
  641. bool UNIT::isDead()
  642. {
  643.     if(!m_dead || m_pTerrain == NULL)return false;
  644.  
  645.     D3DXVECTOR3 worldPos = m_pTerrain->GetWorldPos(m_mappos);
  646.     return m_dead && (worldPos.y - m_position.y) > 3.0f;
  647. }
  648.  
  649. void UNIT::Damage(int dmg, MAPOBJECT* attacker)
  650. {
  651.     if(m_dead)return;
  652.  
  653.     try
  654.     {
  655.         m_hp -= dmg;
  656.  
  657.         if(m_hp <= 0)
  658.         {
  659.             m_hp = 0;
  660.             m_dead = true;
  661.  
  662.             UNIT *attackUnit = (UNIT*)attacker;
  663.             if(attackUnit != NULL && attackUnit->m_pPlayer != NULL)
  664.                 attackUnit->m_pPlayer->m_numKills++;
  665.  
  666.             //Play m_dead animation
  667.             SetAnimation("Die");
  668.             m_time = 0.0f;
  669.             m_pTerrain->m_updateSight = true;
  670.             
  671.             //Reset mapTile->m_pMapObject to NULL
  672.             if(m_pTerrain != NULL)
  673.             {
  674.                 MAPTILE *tile = m_pTerrain->GetTile(m_mappos);
  675.                 if(tile != NULL)tile->m_pMapObject = NULL;
  676.             }
  677.  
  678.             //Leave group
  679.             if(m_pGroup != NULL)
  680.             {
  681.                 m_pGroup->RemoveMember(this);
  682.                 m_pGroup = NULL;
  683.             }
  684.         }
  685.     }
  686.     catch(...)
  687.     {
  688.         debug.Print("Error in UNIT::Damage()");
  689.     }
  690. }
  691.  
  692. void UNIT::Heal()
  693. {
  694.     //Heal Spell
  695.     if(m_type == MAGICIAN && m_mana > 40.0f)
  696.     {
  697.         RECT r = GetMapRect(m_sightRadius - 1);
  698.         UNIT *bestUnit = NULL;
  699.         float healthPrc = 0.6f;
  700.  
  701.         //Find the friendly unit with the lowest health
  702.         for(int y=r.top;y<=r.bottom;y++)
  703.             for(int x=r.left;x<=r.right;x++)
  704.             {
  705.                 MAPTILE* tile = m_pTerrain->GetTile(x, y);
  706.  
  707.                 if(tile != NULL && tile->m_pMapObject != NULL && !tile->m_pMapObject->m_dead &&
  708.                     tile->m_pMapObject->m_team == m_team && !tile->m_pMapObject->m_isBuilding &&
  709.                     tile->m_pMapObject->m_hp < tile->m_pMapObject->m_hpMax)
  710.                 {    
  711.                     float h = tile->m_pMapObject->m_hp / (float)tile->m_pMapObject->m_hpMax;
  712.  
  713.                     if(h < healthPrc)
  714.                     {
  715.                         healthPrc = h;
  716.                         bestUnit = (UNIT*)tile->m_pMapObject;
  717.                     }
  718.                 }
  719.             }
  720.  
  721.         //Cast Heal Spell
  722.         if(bestUnit != NULL)
  723.         {
  724.             bestUnit->m_hp += bestUnit->m_hpMax * 0.5f;
  725.             if(bestUnit->m_hp > bestUnit->m_hpMax)
  726.                 bestUnit->m_hp = bestUnit->m_hpMax;
  727.  
  728.             effects.push_back(new EFFECT_SPELL(m_pDevice, bestUnit->m_position + D3DXVECTOR3(0.0f, 0.1f, 0.0f)));
  729.             Pause(3.0f);
  730.             bestUnit->Pause(5.0f);
  731.             bestUnit->m_animation = ANIM_STILL;            
  732.             m_mana -= 40.0f;
  733.         }
  734.     }
  735. }